package com.wonderful.mall.seckill.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.wonderful.common.to.mq.SeckillOrderTo;
import com.wonderful.common.utils.R;
import com.wonderful.common.vo.MemberRespVo;
import com.wonderful.mall.seckill.entity.SeckillOrderEntity;
import com.wonderful.mall.seckill.feign.CouponFeignService;
import com.wonderful.mall.seckill.feign.ProductFeignService;
import com.wonderful.mall.seckill.interceptor.LoginUserInterceptor;
import com.wonderful.mall.seckill.service.SecKillService;
import com.wonderful.mall.seckill.service.SeckillOrderService;
import com.wonderful.mall.seckill.to.SeckillSkuRedisTo;
import com.wonderful.mall.seckill.vo.SecKillSessionWithSku;
import com.wonderful.mall.seckill.vo.SkuInfoVo;
import org.jetbrains.annotations.NotNull;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class SecKillServiceImpl implements SecKillService {
    @Resource
    CouponFeignService couponFeignService;
    @Resource
    StringRedisTemplate stringRedisTemplate;
    @Resource
    ProductFeignService productFeignService;
    @Resource
    RedissonClient redissonClient;
    @Resource
    RabbitTemplate rabbitTemplate;
    @Resource
    SeckillOrderService seckillOrderService;

    private final String SESSIONS_CACHE_PREFIX = "secKill:sessions:";
    private final String SECKILL_CACHE_PREFIX = "secKill:sku";

    private final String SKU_STOCK_SEMAPHORE = "secKill:stock:";

    @Override
    public void uploadSecKillLatest3Days() {
        R r = couponFeignService.getLatest3DaysSession();
        if (r.getCode() == 0) {
            List<SecKillSessionWithSku> data = r.getData(new TypeReference<List<SecKillSessionWithSku>>() {
            });
            //缓存到redis
            saveSessionInfos(data);
            saveSessionSkuInfos(data);
        }
    }

    @Override
    public List<SeckillSkuRedisTo> getCurrentSeckillSkus() {

        // 1.确定当前时间有哪些秒杀场次
        Set<String> keys = stringRedisTemplate.keys(SESSIONS_CACHE_PREFIX + "*");
        if (CollectionUtils.isNotEmpty(keys)) {
            // 该时间段的所有秒杀
//            ArrayList<List<SeckillSkuRedisTo>> lists = new ArrayList<>();
            List<String> skuIds = new ArrayList<>();
            for (String key : keys) {
                String replace = key.replace(SESSIONS_CACHE_PREFIX, "");
                String[] split = replace.split("_");
                // 0-startTime 1-endTime 2-sessionId
                long now = new Date().getTime();
                long start = Long.parseLong(split[0]);
                long end = Long.parseLong(split[1]);
                if (now >= start && now <= end) {
                    // 锁定这个场次的商品sku ids
                    List<String> range = stringRedisTemplate.opsForList().range(key, -100, 100);
                    if (range != null && range.size() > 0)
                        skuIds.addAll(range);
                }
            }
            BoundHashOperations<String, String, String> hashOps =
                    stringRedisTemplate.boundHashOps(SECKILL_CACHE_PREFIX);
            // hash结构的values
            List<String> list = hashOps.multiGet(skuIds);
            if (list != null) {
                return list.stream().map(item -> JSON.parseObject(item, SeckillSkuRedisTo.class)).collect(Collectors.toList());
            }
        }
        return null;
    }

    @Override
    public SeckillSkuRedisTo getSkuSecKillInfo(Long skuId) {
        R r = couponFeignService.getSkuSecKillInfo(skuId);
        if (r.getCode() == 0) {
            return r.getData(new TypeReference<SeckillSkuRedisTo>() {
            });
        }
        return null;
    }

    @Override
    public String kill(String killId, String num) {
        long start = System.currentTimeMillis();
        SeckillOrderEntity seckillOrderEntity = new SeckillOrderEntity();
        seckillOrderEntity.setCreateTime(new Date());
        //获取会员信息
        MemberRespVo respVo = LoginUserInterceptor.loginUser.get();
        seckillOrderEntity.setMemberId(respVo.getId());
        //获取当前秒杀商品的详细信息
        seckillOrderEntity.setNum(Integer.parseInt(num));
        String orderSn = IdWorker.getTimeId().substring(0,20);
        seckillOrderEntity.setOrderSn(orderSn);
        R r = couponFeignService.getSkuSecKillInfo(Long.parseLong(killId.split("_")[1]));
        if (r.getCode()==0){
            SeckillSkuRedisTo data = r.getData(new TypeReference<SeckillSkuRedisTo>() {
            });
            seckillOrderEntity.setSeckillPrice(data.getSeckillPrice());
            seckillOrderEntity.setPromotionSessionId(data.getPromotionSessionId());
            seckillOrderEntity.setSkuId(data.getSkuId());
        }
        SeckillOrderTo seckillOrderTo = new SeckillOrderTo();
        BeanUtils.copyProperties(seckillOrderEntity,seckillOrderTo);
        rabbitTemplate.convertAndSend("order-event-exchange", "order.seckill.order", seckillOrderTo);
        seckillOrderService.save(seckillOrderEntity);
        long end = System.currentTimeMillis();
        long cost = end - start;
        System.out.println(cost);
        return orderSn;
    }

    /**
     * 缓存活动信息
     */
    private void saveSessionInfos(@NotNull List<SecKillSessionWithSku> session) {
        session.forEach(session1 -> {
            long startTime = session1.getStartTime().getTime();
            long endTime = session1.getEndTime().getTime();
            String key = SESSIONS_CACHE_PREFIX + startTime + "_" + endTime;

            if (Boolean.FALSE.equals(stringRedisTemplate.hasKey(key))) {
                List<String> collect = session1.getRelationEntities().stream().map(item -> item.getPromotionSessionId() + "_" + item.getSkuId().toString()).collect(Collectors.toList());
                stringRedisTemplate.opsForList().leftPushAll(key, collect);
            }
        });
    }

    private void saveSessionSkuInfos(@NotNull List<SecKillSessionWithSku> session) {
        session.forEach(session1 -> {
            BoundHashOperations<String, Object, Object> ops = stringRedisTemplate.boundHashOps(SECKILL_CACHE_PREFIX);
            session1.getRelationEntities().forEach(secKillSkuVo -> {
                //随机码
                String token = UUID.randomUUID().toString().replace("_", "");

                if (!Boolean.TRUE.equals(ops.hasKey(secKillSkuVo.getPromotionSessionId() + "_" + secKillSkuVo.getSkuId().toString()))) {
                    //缓存商品信息
                    SeckillSkuRedisTo seckillSkuRedisTo = new SeckillSkuRedisTo();
                    //sku基本数据
                    R r = productFeignService.info(secKillSkuVo.getSkuId());
                    if (r.getCode() == 0) {
                        SkuInfoVo skuInfo = r.getData("skuInfo", new TypeReference<SkuInfoVo>() {
                        });
                        seckillSkuRedisTo.setSkuInfoVo(skuInfo);
                    }
                    //sku秒杀数据
                    BeanUtils.copyProperties(secKillSkuVo, seckillSkuRedisTo);

                    seckillSkuRedisTo.setStartTime(session1.getStartTime().getTime());
                    seckillSkuRedisTo.setEndTime(session1.getEndTime().getTime());

                    seckillSkuRedisTo.setRandomCode(token);
                    String s = JSON.toJSONString(seckillSkuRedisTo);
                    ops.put(secKillSkuVo.getPromotionSessionId() + "_" + secKillSkuVo.getSkuId().toString(), s);
                    RSemaphore semaphore = redissonClient.getSemaphore(SKU_STOCK_SEMAPHORE + token);
                    semaphore.trySetPermits(secKillSkuVo.getSeckillCount());
                }
            });
        });
    }
}
